
'''Dynamic method call management for the Remember The Milk API.


Thanks to Lucio Torre and David Weil (tenuki) from PyAr for the naming
suggestions for this module.
'''

import logging

logging.basicConfig()
LOG = logging.getLogger(__name__)
LOG.setLevel(logging.INFO)

__all__ = ('CallSink', )

class CallSink(object):
    '''The Call Sink.

    You can get arbitrary attributes from this object; every attribute
    is a new instance of CallSink which "remembers" who its parents are.
    
    If any of these attributes are called, a "real" call handler is
    called. The call handler is called with the same arguments, plus a
    keyord argument 'metehod' containing the whole call chain.

    Example:

    >>> from callsink import CallSink
    >>> def my_handler(**args):
    ...     print "Calling my_handler(%s)" % args
    ... 
    >>> spam = CallSink(my_handler, 'spam')
    >>> spam.eggs(msg='hi there!')
    Calling my_handler({'msg': 'hi there!', 'method': 'spam.eggs'})
    >>> spam.eggs.more_spam(id=3, foo='bar')
    Calling my_handler({'foo': 'bar', 'id': 3, 'method': 'spam.eggs.more_spam'})
    >>> 
    '''

    def __init__(self, call_handler, call_chain):
        self._call_handler = call_handler
        self._call_chain = call_chain

    def __getattr__(self, attrib):
        # Refuse to act as a proxy for unimplemented special methods
        if attrib.startswith('_'):
            raise AttributeError("No such attribute '%s'" % attrib)

        return CallSink(self._call_handler, '.'.join((self._call_chain, attrib)))

    def __call__(self, **args):
        args['method'] = self._call_chain
        LOG.debug("Calling %s(%s)" % (self._call_chain, args))
        return self._call_handler(**args)

if __name__ == "__main__":
    import doctest
    doctest.testmod()

